package com.andova.toolkit.net;

import android.util.Log;
import android.view.View;

import androidx.annotation.NonNull;

import com.andova.common.BuildConfig;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;

import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;

import okhttp3.MediaType;
import okhttp3.Request;
import okio.Buffer;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

/**
 * Created by Zsago on 2017/8/5.
 *
 * @author Zsago
 */
@Deprecated
abstract class NetCallback<T extends BaseResponseModel> implements Callback<T> {
    /**
     * 服务端响应出错信息
     */
    public static final int CALLBACK_CODE_SERVER_ERROR = 0x50;
    /**
     * 服务端响应的内容数据为空
     */
    public static final int CALLBACK_CODE_RESPONSE_EMPTY = 0x51;
    public static final int CALLBACK_CODE_RESPONSE_SUCCESS = 0x52;
    /**
     * 响应结果异常
     */
    public static final int CALLBACK_CODE_RESPONSE_ERROR = 0x53;
    /**
     * 数据解析异常
     */
    public static final int CALLBACK_CODE_RESPONSE_ERROR_JSON = 0x54;
    public static final int CALLBACK_CODE_RESPONSE_ERROR_TIME_OUT = 0x55;
    public static final int CALLBACK_CODE_RESPONSE_ERROR_CONNECTION = 0x56;

    public static final String TAG_NETWORK = "TAG_NETWORK";

    public void call(Call<T> call) {
        ready();
        call.enqueue(this);
    }

    @Override
    public void onResponse(@NonNull Call<T> call, @NonNull Response<T> response) {
        if (BuildConfig.DEBUG) printRequest(response.raw().request());
        if (!response.isSuccessful() || response.body() != null && !response.body().isSuccess()) {
            complete(CALLBACK_CODE_SERVER_ERROR);
            try {
                String message = convertErrorBody(response, model()).getMessage();
                error(response.code(), message);
            } catch (NullPointerException e) {
                e.printStackTrace();
            }
            return;
        }
        try {
            if (response.body().isEmpty()) {
                complete(CALLBACK_CODE_RESPONSE_EMPTY);
                return;
            }
            complete(CALLBACK_CODE_RESPONSE_SUCCESS);
            handle(response.body());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onFailure(@NonNull Call<T> call, @NonNull Throwable throwable) {
        if (throwable instanceof JsonSyntaxException) {
            complete(CALLBACK_CODE_RESPONSE_ERROR_JSON);
        } else if (throwable instanceof SocketTimeoutException) {
            complete(CALLBACK_CODE_RESPONSE_ERROR_TIME_OUT);
        } else if (throwable instanceof ConnectException) {
            complete(CALLBACK_CODE_RESPONSE_ERROR_CONNECTION);
        } else {
            complete(CALLBACK_CODE_RESPONSE_ERROR);
        }
    }

    private void printRequest(Request request) {
        try {
            Buffer buffer = new Buffer();
            request.body().writeTo(buffer);

            Charset charset = Charset.forName("UTF-8");
            MediaType contentType = request.body().contentType();
            if (contentType != null) {
                charset = contentType.charset(Charset.forName("UTF-8"));
            }
            Log.i(TAG_NETWORK, "the request body is:" + request.toString() + "\nthe request body is:" + buffer.readString(charset));
        } catch (IOException | NullPointerException e) {
            Log.i(TAG_NETWORK, "the request body is:" + request.toString());
        }
    }

    private T convertErrorBody(Response<T> response, Class<T> obj) {
        InputStream is = null;
        try {
            if (response.code() < 400 || response.code() > 500) return null;
            try {
                is = response.errorBody().byteStream();
                String data = streamToString(is);
                return new Gson().fromJson(data, obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    private String streamToString(InputStream stream) {
        byte buffer[] = new byte[1024];
        int len;
        StringBuilder sb = new StringBuilder();
        try {
            while ((len = stream.read(buffer)) != -1) {
                sb.append(new String(buffer, 0, len));
            }
            stream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    abstract View view();

    abstract Class<T> model();

    abstract void ready();

    abstract void handle(T respBody);

    abstract void complete(int code);

    abstract void error(int code, String error);
}
